﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Meebey.SmartIrc4net;
using System.Threading;
using System.Collections;
using System.Net;
using System.Text.RegularExpressions;
using ChatterBotAPI;

namespace boten
{
    public partial class Main : Form
    {
        public IrcClient ChatClient { get; set; }
        public IrcCommands Commands { get; set; }
        public string IdentPW { get; set; }
        public string Version { get; set; }
        public WebClient BotWebClient { get; set; }
        public Random DiceRoller { get; set; }
        public List<KeyValuePair<string,string>> MessageBox { get; set; }
        public List<KeyValuePair<string, string>> PrivateMessageBox { get; set; }
        public ChatterBot CleverBot { get; set; }
        public ChatterBotSession ChatSession { get; set; }
        public bool BotPartakes { get; set; }

        public Main()
        {
            InitializeComponent();
            ChatClient = new IrcClient();
            Commands = new IrcCommands();
            var cbf = new ChatterBotFactory();
            CleverBot = cbf.Create(ChatterBotType.CLEVERBOT);
            ChatSession = CleverBot.CreateSession();
            ChatClient.ActiveChannelSyncing = true;
            BotWebClient = new WebClient();
            DiceRoller = new Random();
            MessageBox = new List<KeyValuePair<string,string>>();
            PrivateMessageBox = new List<KeyValuePair<string, string>>(); 
            BotPartakes = false;
            this.Version = "~TinyBot 0.8b by FLClover~ Bug Reports to: clover@semperfidelis.eu";
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            tbChannel.Enabled = false;
            tbPort.Enabled = false;
            tbServer.Enabled = false;
            tbUserName.Enabled = false;
            btnStart.Enabled = false;
            btnDisconnect.Enabled = true;
            tbPassword.Enabled = false;
            tbNSAuth.Enabled = false;
            tbDescription.Enabled = false;
            
            var port = Convert.ToInt32(tbPort.Text);
            
            ChatClient.OnConnected += OnChatClientConnected;
            ChatClient.OnChannelMessage += OnChannelMessage;
            ChatClient.OnOp += (ChatClient_OnOp);
            ChatClient.OnQueryMessage += new IrcEventHandler(ChatClient_OnQueryMessage);
            ChatClient.OnCtcpReply += new CtcpEventHandler(ChatClient_OnCtcpReply);
            ChatClient.OnError += new ErrorEventHandler(ChatClient_OnError);

            ChatClient.Connect(tbServer.Text, port);
        }

        void ChatClient_OnQueryMessage(object sender, IrcEventArgs e)
        {
            if (e.Data.Message.StartsWith("!"))
            {
                var pattern = new Regex(@"(?<Command>![\w]*)");
                var match = pattern.Match(e.Data.Message);
                var command = match.Groups["Command"].Value.ToLower();
                var param = e.Data.Message.Replace(command, "").Trim();

                if (command == "!pm" || command == "!w")
                {
                    var tellTarget = param.Substring(0, param.IndexOf(" ")).Trim();
                    var tmp = param.Replace(tellTarget, "").Trim();
                    var message = string.Format("{0}, {1} said: {2} ({3})", tellTarget, e.Data.Nick, tmp, DateTime.Now.ToString());
                    var toAdd = new KeyValuePair<string, string>(tellTarget, message);
                    PrivateMessageBox.Add(toAdd); 
                }
            }
        }

        void ChatClient_OnError(object sender, ErrorEventArgs e)
        {
            AddMessage(e.ErrorMessage);
        }

        void ChatClient_OnCtcpReply(object sender, CtcpEventArgs e)
        {
            AddMessage(e.CtcpCommand + " " + e.CtcpParameter);
            AddMessage(e.Data.Message);
        }

        void ChatClient_OnOp(object sender, OpEventArgs e)
        {
            AddMessage("Got OP");
        }

        public void OnChatClientConnected(object Sender, EventArgs e)
        { 
            //On connect join channel
            AddMessage("Connected!");
            ChatClient.WriteLine(Rfc2812.Nick(tbUserName.Text), Priority.Critical);
            ChatClient.WriteLine(Rfc2812.User(tbUserName.Text, 0, tbDescription.Text), Priority.Critical);            
            
            ChatClient.OnJoin += (ChatClient_OnJoin);
            ChatClient.WriteLine(Rfc2812.Join(tbChannel.Text,tbPassword.Text));

            if (tbNSAuth.Text != string.Empty)
            {
                var desc = tbDescription.Text;
                if (desc == string.Empty)
                    desc = "TinyBot Instance";
                    
                ChatClient.Login(tbUserName.Text, desc, 0, "TinyBot", tbNSAuth.Text);
            }
            
            listenTimer.Enabled = true;
        }

        void ChatClient_OnJoin(object sender, JoinEventArgs e)
        {
            UsersToList();
        }

        private void UsersToList()
        {
            lbUsers.Items.Clear();
            var usrs = ChatClient.GetChannel(tbChannel.Text).Users;
            
            foreach (DictionaryEntry item in usrs)
            {
                lbUsers.Items.Add(item.Key);
            }
            lbUsers.Sorted = true;
        }

        public void OnChannelMessage(object Sender, EventArgs e)
        {
            var ea = (IrcEventArgs)e;
            Deliver(ea.Data.Nick);

            if (BotPartakes)
            {
                CleverBotHandler(ea.Data.Message);
            }

            if (ea.Data.Message.StartsWith("!") && !ea.Data.Message.EndsWith("!"))
            {
                QueryHandler(ea);    
            }

            if (ea.Data.Message.ToLower().Contains("http://"))
            {
                if (!ea.Data.Message.ToLower().StartsWith("page info"))
                    URLHandler(ea);
            }

            var chatMs = string.Format("<{0}> : {1}", ea.Data.Nick, ea.Data.Message);
            AddMessage(chatMs);
        }

        private void URLHandler(IrcEventArgs ea)
        {
            var r = new Regex(@"(?<Protocol>\w+):\/\/(?<Domain>[\w@][\w.:@]+)\/?[\w\.?=%&=\-@/$,]*");
            var page = r.Match(ea.Data.Message);
            
            try 
	        {
                var source = BotWebClient.DownloadString(page.Value);
                var title = Regex.Match(source, @"\<title\b[^>]*\>\s*(?<Title>[\s\S]*?)\</title\>", RegexOptions.IgnoreCase).Groups["Title"].Value;
                title = System.Web.HttpUtility.HtmlDecode(title);
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, string.Format("Page Info: {0}", title));   
	        }
	        catch (Exception)
	        {
                //Do nothing Jon Snow
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, string.Format("Page unreachable. Sorry."));   
	        }
        }

        private void QueryHandler(IrcEventArgs ea)
        {
            //var parts = ea.Data.Message.Replace("!", "").
            var pattern = new Regex(@"(?<Command>![\w]*)");
            var match = pattern.Match(ea.Data.Message);
            var command = match.Groups["Command"].Value.ToLower();
            var param = ea.Data.Message.Replace(command, "").Trim();

            if (command == "!version")
            {
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, this.Version);        
            }
            else if (command == "!roll")
            {
                var r = new Regex(@"(?<NumDice>[\d]*)[dD](?<Sides>[\d]*)");
                var numberOfDice = Convert.ToInt32(r.Match(param).Groups["NumDice"].Value);
                var diceSides = Convert.ToInt32(r.Match(param).Groups["Sides"].Value);
                var result = 0;
                var details = "";
                if (numberOfDice < 100 && diceSides < 1000)
                {
                    for (int i = 0; i < numberOfDice; i++)
                    {
                        var roll = DiceRoller.Next(1, diceSides);
                        result = result + roll;
                        details = details + roll.ToString() + ",";
                    }
                    details = details.Substring(0, details.Length - 1); //Last Comma

                    if (details.Length > 300)
                        details = "Too many rolls for display!";

                    var msg = string.Format("{0} rolled {1} {2}-sided dice and got a {3}! ( {4} )", ea.Data.Nick, numberOfDice, diceSides, result, details);
                    ChatClient.SendMessage(SendType.Message, tbChannel.Text, msg);
                }
                else
                {
                    ChatClient.SendMessage(SendType.Message, tbChannel.Text, "That's too much for me. How about something that makes sense?");
                }
            }
            else if (command == "!c")
            {
                CleverBotHandler(ea.Data.Nick, param);
            }
            else if (command == "!tell")
            {
                var tellTarget = param.Substring(0, param.IndexOf(" ")).Trim();
                var tmp = param.Replace(tellTarget, "").Trim();
                var message = string.Format("{0}, {1} said: {2} ({3})", tellTarget, ea.Data.Nick, tmp, DateTime.Now.ToString());
                var toAdd = new KeyValuePair<string, string>(tellTarget, message);
                MessageBox.Add(toAdd);
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, "OK.");
            }
            else if (command == "!ai_partake")
            {
                BotPartakes = true;
                var thought = ChatSession.Think(new ChatterBotThought());
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, "I WILL PARTAKE IN THIS CONVERSATION!!");
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, thought.Text);
            }
            else if (command == "!ai_shutup")
            {
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, "Alright, I'll shut up. :(");
                BotPartakes = false;
            }
            else if (command == "!help")
            {
                var output = "Commands available: !tell, !roll, !ai_partake, !ai_shutup, !c, !version, !help";
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, output);
            }
            else
            {
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, "Unrecognized command: " + command);
            }
        }

        private void CleverBotHandler(string name, string message)
        {
            try
            {
                var response = string.Format("{0}: {1}", name, ChatSession.Think(message));
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, response);
            }
            catch (Exception)
            {
                BotPartakes = false;
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, "I don't want to talk right now.");
            }    
        }

        private void CleverBotHandler(string message)
        {
            try
            {
                var response = ChatSession.Think(message);
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, response);
            }
            catch (Exception)
            {
                BotPartakes = false;
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, "I don't want to talk right now.");
            }            
        }

        private void Deliver(string name)
        {
            //public messages
            var toClear = new List<int>();
            var messages = from message in MessageBox
                where message.Key.ToLower() == name.ToLower()
                select message;

            foreach (var item in messages)
            {
                ChatClient.SendMessage(SendType.Message, tbChannel.Text, item.Value);
                toClear.Add(MessageBox.IndexOf(item));
            }

            foreach (var index in toClear)
            {
                MessageBox.Remove(MessageBox[index]);
            }

            //private messages
            toClear = new List<int>();
            messages = from message in PrivateMessageBox
                           where message.Key.ToLower() == name.ToLower()
                           select message;

            foreach (var item in messages)
            {
                //ChatClient.SendMessage(SendType.Message, item.Key, item.Value);
                ChatClient.WriteLine(string.Format("/q {0}",item.Key));
                ChatClient.SendMessage(SendType.Message, item.Key, item.Value);
                toClear.Add(PrivateMessageBox.IndexOf(item));
            }

            foreach (var index in toClear)
            {
                PrivateMessageBox.Remove(PrivateMessageBox[index]);
            }

        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            if (ChatClient.IsConnected)
            {
                listenTimer.Enabled = true;
                ChatClient.Disconnect();
                AddMessage("Disconnected by user");

                ChatClient.OnConnected -= OnChatClientConnected;
                ChatClient.OnChannelMessage -= OnChannelMessage;

                tbChannel.Enabled = true;
                tbPort.Enabled = true;
                tbServer.Enabled = true;
                tbUserName.Enabled = true;
                btnStart.Enabled = true;
                btnDisconnect.Enabled = false;
                tbPassword.Enabled = true;
                tbNSAuth.Enabled = true;
                tbDescription.Enabled = true;
            }
        }

        private void AddMessage(string message)
        {
            if (string.Empty != message)
            {
                rtbMessages.AppendText("\n");

                var msg = string.Format("<{0}> {1}", DateTime.Now.ToShortTimeString(), message);
                rtbMessages.AppendText(msg);
            }
        }

        private void listenTimer_Tick(object sender, EventArgs e)
        {
            ChatClient.ListenOnce(false);
        }

        private void tbSendText_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                btnSend_Click(sender, e);
            }
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            if (ChatClient.IsConnected)
            {
                if (!tbSendText.Text.StartsWith("/"))
                {
                    ChatClient.SendMessage(SendType.Message, tbChannel.Text, tbSendText.Text);

                    var msg = string.Format("<{0}> : {1}", tbUserName.Text, tbSendText.Text);
                    AddMessage(msg);
                }
                else
                {
                    ChatClient.WriteLine(tbSendText.Text);
                    AddMessage(tbSendText.Text);
                }
                tbSendText.Text = "";
            }
        }
    }
}   
